#define _J_DOGADJAJI_C_
#include "dogadjaji.h"

#include "postavke.h"
#include "razno.h"
#include "liste.h"
#include "ispis.h"
#include "red.h"
#include "dretve.h"
#include "procesi.h"
#include "vrijeme.h"
#include "spremnik.h"
#include <arh/kontekst.h>
#include <pozivi_jezgre.h>


//redovi dogadjaja
ZAUZMI(j_red_dogadjaja_t, j_red_dog, NAJ_REDADOG);

//prijave za dogadjaje (redove)
ZAUZMI(j_prijava_dog_t, j_prijava_dog, NAJ_PRIJAVA_DOG);

ZAUZMI(j_dogadjaj_t, j_dog, NAJ_DOG);

void j_dog_inic_podatke_dretve(j_red_dog_dretve_t *red)
{
	red->red_dog.prvi = NULL;
	red->red_dog.zadnji = NULL;
	red->funkcija = NULL;
	red->param = NULL;
	red->red = -1;
}

//stvori novi red dogadjaja
int j_stvori_red_dogadjaja (void *param)
{
	struct {
		int zast;
		int prio;
	} *p = param;

	int id;
	
	PARAMNULL(p);

	if ( (p->zast & 1) + ((p->zast>>1)&1) + ((p->zast>>2)&1) != 1) {
		j_ispisi("Samo jedna od zastavica PO_REDU, SVIMA ili NIKOME "
				"moze biti postavljena!\n");
		return -1;
	}
	if ( (p->zast & DOG_NIKOME) && (p->zast & DOG_MICE_SE) ) {
		j_ispisi("Zastavice NIKOME i MICE_SE ne idu zajedno!\n");
		return -1;
	}
	
	id = DOHVATI(j_red_dog);
	
	if (id != -1) {
		j_red_dog[id].id = id;
		j_red_dog[id].zast = p->zast;
		j_red_dog[id].prio = p->prio;
		
		j_red_dog[id].prijave.prvi = NULL;
		j_red_dog[id].prijave.zadnji = NULL;

		j_red_dog[id].red_dog.prvi = NULL;
		j_red_dog[id].red_dog.zadnji = NULL;
		j_red_dog[id].red = -1;
	}
	else {
		j_ispisi("Nema slobodna opisnika za red dogadjaja!\n");
	}

	return id;
}

//brise red dogadjaja identificiran sa id-jem koji je jedini parametar
int j_obrisi_red_dogadjaja (void *param)
{
	int *pid = param, id;
	int br = 0;
	j_dogadjaj_t *dog;
	j_prijava_dog_t *p;
	
	PARAMNULL(pid);
	
	id = *pid;

	if (OBRISI(j_red_dog, id) == -1) {
		j_ispisi("Krivi id reda ili red nije zauzet (%d)\n", id);
		return -1;
	}
	
	//obrisi sve dogadjaje
	dog = UZMI_PRVI_ELEMENT(&j_red_dog[id].red_dog);
	while (dog != NULL) {
		OBRISI(j_dog, (dog - j_dog));

		dog = UZMI_PRVI_ELEMENT(&j_red_dog[id].red_dog);
	}

	//obrisi sve prijave
	p = UZMI_PRVI_ELEMENT(&j_red_dog[id].prijave);
	while (p != NULL) {
		OBRISI(j_prijava_dog, (p - j_prijava_dog));
		
		p = UZMI_PRVI_ELEMENT(&j_red_dog[id].prijave);
	}

	//ako ima blokiranih dretvi, otpusti ih
	if (j_red_dog[id].red > 0)
		br = j_propusti_iz_reda(j_red_dog[id].red, ISTINA);

	return br;
}

//dretva se prijavljuje na red dogadjaja:
//globalni ili lokalni (kada je id RED_DOG_DRETVE)
int j_prijavi_se_na_red_dogadjaja (void *param)
{
	struct {
		int id;
		void (*funkcija) (dogadjaj_t *, void *);
		void *param;
	} *p = param;
	j_red_dog_dretve_t *red_dog;
	j_prijava_dog_t *prijava;

	int idp;
	
	PARAMNULL(p);
	
	if (p->id == RED_DOG_DRETVE) {
		red_dog = j_opdr_red_dog(j_aktivna_dretva());
		red_dog->funkcija = p->funkcija;
		red_dog->param = p->param;
	}
	else {
		//provjeri da red postoji
		if (PROVJERI_SLOBODNO(j_red_dog, p->id)) {
			j_ispisi("Krivi id reda dogadjaja (%d)\n", p->id);
			return -1;
		}
		
		//dohvati objekt prijave
		idp = DOHVATI(j_prijava_dog);
		if (idp == -1) {
			j_ispisi("Nema slobodna opisnika za "
					"prijavu na dogadjaj!\n");
			return -1;
		}

		prijava = &j_prijava_dog[idp];

		prijava->id_dretve = j_opdr_id(j_aktivna_dretva());
		prijava->funkcija = p->funkcija;
		prijava->param = p->param;
		
		DODAJ_U_SORT_LISTU(&j_red_dog[p->id].prijave, prijava,
					DOG_USPOREDI_DRETVE);
	}

	return 0;
}

//dretva stvara dogadjaj i salje ga ili u neki globalni red ili izravno dretvi
int j_postavi_dogadjaj (void *param)
{
	struct {
		int tip;
		int param;
		void *pparam;
		int prio;
		int kome;
		int id;
	} *p = param;
	
	int dprio, idd;
	j_red_dogadjaja_t *red = NULL;
	j_dogadjaj_t *dog;
	j_dretva_t *opd = NULL;
	j_prijava_dog_t *prijava = NULL, *prva_prijava;
	bool kopiraj = NEISTINA;
	
	PARAMNULL(p);

	dprio = p->prio;

	switch(p->kome) {
	case DOG_ZA_DRETVU:
		//provjeri da li dretva postoji
		opd = j_dohvati_opisnik(p->id);
		if (opd == NULL) {
			//j_ispisi("Dretva ne postoji (%d)\n", p->id);
			return -1;
		}
		break;

	case DOG_ZA_RED:
		//provjeri da li red postoji
		if (PROVJERI_SLOBODNO(j_red_dog, p->id)) {
			j_ispisi("Krivi id reda dogadjaja (%d)\n", p->id);
			return -1;
		}
		
		//ako je prioritet manji od zadanog u redu, povecaj ga
		if (dprio > 0 && j_red_dog[p->id].prio > 0 && 
						dprio > j_red_dog[p->id].prio)
			dprio = j_red_dog[p->id].prio;

		break;

	default:
		j_ispisi("Parametar 'kome' krivi (%d)\n", p->kome);
		return -1;
		break;
	}

	idd = DOHVATI(j_dog);
	if (idd == -1) {
		j_ispisi("Nema slobodna opisnika za stvaranje dogadjaja!\n");
		return -1;
	}
	dog = &j_dog[idd];

	dog->dog.tip = p->tip;
	dog->dog.param = p->param;
	dog->dog.pparam = p->pparam;
	dog->dog.prio = dprio;
	j_dohvati_trenutni_sat(&dog->dog.kada);
	dog->dog.id_gen = j_opdr_id(j_aktivna_dretva());
	
	if (p->kome == DOG_ZA_DRETVU) {
		dog->prijava = NULL;
		kopiraj = j_stavi_dogadjaj_dretvi ( opd, dog,
			((j_red_dog_dretve_t *) j_opdr_red_dog(opd))->red);
	}
	else {	//dogadjaj ide u 'globalni' red
		
		red = &j_red_dog[p->id];

		if (red->zast & DOG_NIKOME) //ne salje se nikome, samo u red
			prijava = NULL; //preskoci iduci 'while'
		else
			prijava = UZMI_PRVI_ELEMENT(&red->prijave);

		prva_prijava = prijava;

		while (prijava != NULL) {
			opd = j_dohvati_opisnik(prijava->id_dretve);
			//ako dretva vise ne postoji, makni prijavu
			if (opd == NULL) {
				OBRISI(j_prijava_dog, prijava - j_prijava_dog);
			}
			else {
				//stvori kopiju (sadasnji 'dog' je potrosen!)
				if (kopiraj)
					dog = j_kopiraj_dogadjaj(dog);

				kopiraj = ISTINA; //potrosit ce se i ovaj 'dog'

				if (dog == NULL) {
					j_ispisi("dog je NULL!\n");
					return -1;
				}

				//stavi dretvu na kraj reda (prema priorietu)
				//tj. njenu prijavu stavi na kraj, obzirom da ce
				//sada dobiti poruku
				DODAJ_U_SORT_LISTU(&red->prijave, prijava,
						DOG_USPOREDI_DRETVE);

				//posalji dogadjaj dretvi
				dog->prijava = prijava;
				j_stavi_dogadjaj_dretvi (opd, dog, red->red);

				if (red->zast & DOG_PO_REDU)
					return 0;//samo prvoj dretvi saljem
				
			}
			
			if (POGLEDAJ_PRVI_ELEMENT(&red->prijave) == prva_prijava)
				break;

			prijava = UZMI_PRVI_ELEMENT(&red->prijave);
		}
		
		//dogadjaj ide u red (stavlja se u listu) ako:
		if ((red->zast & DOG_NIKOME) || (red->zast & DOG_OSTAJE) ||
			( !(red->zast & DOG_MICE_SE) && !kopiraj) )
		{					//nikome se nije slalo
			if (kopiraj)
				dog = j_kopiraj_dogadjaj(dog);
			if (dog == NULL) {
				j_ispisi("dog je NULL!\n");
				return -1;
			}
			kopiraj = ISTINA;

			DODAJ_U_SORT_LISTU(&red->red_dog, dog,
						DOG_USPOREDI_PRIO);
			
			//ako ima dretvi koje cekaju, propusti prvu (ili sve?)
			j_propusti_iz_reda(red->red, ISTINA);
		}
	}
	if (!kopiraj) //'dog' nije potrosen, obrisi ga
		OBRISI(j_dog, dog - j_dog);

	return 0;
}

//pomocna funkcija - umece obradu dretvi ili stavlja dogadjaj u red
static bool j_stavi_dogadjaj_dretvi (void *dretva, j_dogadjaj_t *dog, int red)
{
	void (*funkcija) (dogadjaj_t *, void *);
	void *param;
	j_dretva_t *opd = dretva;
	j_red_dog_dretve_t *red_dog = j_opdr_red_dog(dretva);
	bool odblokiraj = NEISTINA;
	bool obradi = NEISTINA;

	//je li zadana funkcija za obradu dogadjaja?
	if (dog->prijava != NULL) {
		funkcija = dog->prijava->funkcija;
		param = dog->prijava->param;
	}
	else {
		funkcija = red_dog->funkcija;
		param = red_dog->param;
	}

	obradi = (	dog->dog.prio > 0 &&
			dog->dog.prio < j_opdr_prio(opd) && 
			funkcija != NULL
		);

	odblokiraj = (	j_opdr_red(opd) == red ||
			j_opdr_red(opd) == red_dog->red ||
			(j_tip_reda(j_opdr_red(opd)) == REDALARMA && obradi) );


	//dogadjaj budi dretvu?
	if (odblokiraj) {
		if (j_makni_iz_reda(j_opdr_red(opd), opd)) {
			j_ispisi("Nema dretve u redu!?\n");
		}
		else {
			j_samo_stavi_u_pripravne(opd);
			j_aktiviraj_prvu_pripravnu();
		}
	}
	
	//treba odmah obradit dogadjaj?
	if (obradi){
		//umetni stog za poziv obrade dogadjaja
		dog->povratak = arh_dodaj_obradu (
			j_oppr_spremnik (j_opdr_id_procesa ( opd ) ),
			j_opdr_kontekst ( opd ),
			obrada_dogadjaja, 
			( sizeof(funkcija) + sizeof(param) +
			( sizeof(*dog) + sizeof(uint32) - 1) ) / sizeof(uint32), 
			funkcija,
			param,
			*dog );

		//privremeno povecaj prioritet
		j_povecaj_prioritet (	j_opdr_id (opd), dog->dog.prio,
					dog->povratak );

		return NEISTINA; //obrisi objekt dog
	}
	else { //samo stavi u red, dretva ce ga sama procitat
		DODAJ_U_SORT_LISTU(&red_dog->red_dog, dog,
					DOG_USPOREDI_PRIO);

		return ISTINA; //ne brisati dog!
	}	
}

j_dogadjaj_t *j_kopiraj_dogadjaj (j_dogadjaj_t *dog)
{
	j_dogadjaj_t *dog2;
	int idd;

	idd = DOHVATI(j_dog);
	if (idd == -1) {
		j_ispisi("Nema slobodna opisnika za stvaranje dogadjaja!\n");
		return NULL;
	}
	dog2 = &j_dog[idd];

	dog2->dog = dog->dog;

	return dog2;
}

//kada zavrsi obrada poziva se ova jezgrina funkcija (iz j_obrada_dogadjaja)
//ne pozivati ovu funkciju drukcije!
int j_kraj_obrade_dogadjaja (void *_param)
{
	void **param = _param, *povratak;
	j_dogadjaj_t *dog;
	j_dretva_t *opd = j_aktivna_dretva();
	j_red_dog_dretve_t *red_dog = j_opdr_red_dog(opd);

	PARAMNULL(param);

	povratak = *param;

	arh_makni_obradu ( j_oppr_spremnik (j_opdr_id_procesa ( opd ) ),
		j_opdr_kontekst(opd), povratak);

	j_vrati_prioritet(j_opdr_id(opd), povratak);

	//provjeri ima li jos dogadjaja na cekanju u redu dretve
	dog = POGLEDAJ_PRVI_ELEMENT(&red_dog->red_dog);
	if (dog && dog->dog.prio > 0 && dog->dog.prio < j_opdr_prio(opd) && 
		red_dog->funkcija != NULL)
	{
		//sredi stog da se pri povratku u dretvu vrati u obradu
		//ovog dogadjaja
		dog = UZMI_PRVI_ELEMENT(&red_dog->red_dog);

		j_stavi_dogadjaj_dretvi (opd, dog, red_dog->red);
	}

	return 0;
}

//procitaj dogadjaj iz reda
//oznaka reda (id) moze identificirati globalni red ili ako se zeli citati iz
//reda dretve koristi se konstanta RED_DOG_DRETVE
int j_dohvati_dogadjaj (void *param)
{
	struct {
		int id_reda;
		int tip;
		dogadjaj_t *dog;
		int zast;
		vrijeme_t *koliko;
	} *p = param;

	dogadjaj_t *p_dog;
	j_dogadjaj_t *dog;
	j_lista_dogadjaja_t *red;
	j_dretva_t *opd = j_aktivna_dretva();
	j_red_dog_dretve_t *red_dog = j_opdr_red_dog(opd);
	int *redcek;
	vrijeme_t *koliko;
	
	PARAMNULL(p);

	if (p->id_reda == RED_DOG_DRETVE) {
		red = &red_dog->red_dog;
		redcek = &red_dog->red;
	}
	else {
		//provjeri da red postoji
		if (PROVJERI_SLOBODNO(j_red_dog, p->id_reda)) {
			j_ispisi("Krivi id reda dogadjaja (%d)\n", p->id_reda);
			return -1;
		}
		red = &j_red_dog[p->id_reda].red_dog;
		redcek = &j_red_dog[p->id_reda].red;
	}
	
	dog = POGLEDAJ_PRVI_ELEMENT(red);
	
	if (p->tip != 0) { //trazi se dogadjaj zadanog tipa
		while (dog != NULL && dog->dog.tip != p->tip)
			dog = POGLEDAJ_IDUCI_ELEMENT(dog);
	}
	
	if (dog == NULL) { //nema dogadjaja
		if (p->zast & DOG_CEKAJ) {
			if (*redcek == -1) {
				*redcek = j_dohvati_red(DOGADJAJ, SORT_PRIO, 0);
				if (*redcek == -1)
					return -1;
			}
			if (p->koliko != NULL)
				koliko = j_puj(j_aktivni_proces(), p->koliko);
			else
				koliko = NULL;

			j_stavi_u_red(*redcek, opd, koliko);

			return 1;
		}
		else {
			return 1;
		}
	}
	
	p_dog = j_puj(j_aktivni_proces(), p->dog);
	*p_dog = dog->dog; //kopiraj dogadjaj korisniku

	if (!(p->zast & DOG_OSTAVI)) {
		MAKNI_IZ_LISTE(red, dog); //ne mora biti prvi element ('tip')
		OBRISI(j_dog, dog - j_dog);
	}

	return 0;
}